package com.xuliugen.rpc.proxy;

import com.xuliugen.rpc.protocol.InvokerMessage;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author xuliugen
 * @date 2018/6/6
 */
public class RpcProxy {

    /**
     * 创建代理对象
     * @param clazz 接口
     * @param <T>   泛型T
     * @return
     */
    public static <T> T  getProxy(Class<?> clazz) {
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(),
                new Class[]{clazz}, new RpcProxyInvocationHandler(clazz));
    }

    /**
     * JDK动态代理的InvocationHandler实现类
     */
    public static class RpcProxyInvocationHandler implements InvocationHandler {

        private Class<?> clazz;

        public RpcProxyInvocationHandler(Class<?> clazz) {
            this.clazz = clazz;
        }

        /**
         * InvocationHandler代理对象实现类重写的方法invoke
         * 动态代理实例对象必须要走的一个转发请求方法
         * @param proxy  代理对象
         * @param method 代理的方法
         * @param args   代理的参数
         * @return
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) {

            if (Object.class.equals(method.getDeclaringClass())) {
                try {
                    //执行本地方法
                    return method.invoke(this, args);
                } catch (Throwable t) {
                    t.printStackTrace();
                }
            } else {
                //执行远程方法
                return rpcInvoke(proxy, method, args);
            }
            return null;
        }

        /**
         * 实现接口的远程调用
         * @param proxy
         * @param method
         * @param args
         * @return
         */
        private Object rpcInvoke(Object proxy, Method method, Object[] args) {

            //将Request请求信息封装到InvokeMessage协议对象中
            InvokerMessage message = new InvokerMessage();

            //封装数据对象到协议对象中
            message.setClassName(this.clazz.getName());
            message.setMethodName(method.getName());
            message.setParamTypes(method.getParameterTypes());
            message.setParamValues(args);
            return executeInvoke(proxy, message);
        }

        /**
         * 执行具体的Invoke请求
         * @param proxy
         * @param message
         */
        private Object executeInvoke(Object proxy, InvokerMessage message) {

            final RpcProxyHandler consumerHandler = new RpcProxyHandler();

            //Netty构造一个远程异步传输桥梁
            EventLoopGroup group = new NioEventLoopGroup();
            try {
                Bootstrap boot = new Bootstrap();
                boot.group(group)
                        .channel(NioSocketChannel.class)
                        .option(ChannelOption.TCP_NODELAY, true)
                        .handler(new ChannelInitializer<SocketChannel>() {

                            @Override
                            protected void initChannel(SocketChannel channel) {
                                ChannelPipeline pipeline = channel.pipeline();
                                pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
                                pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
                                //底层传输是字节协议流，字节协议流转化我们Netty的处理对象
                                //对象自动序列化和反序列化
                                pipeline.addLast("encoder", new ObjectEncoder());
                                pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));

                                //处理远程服务返回信息Handler
                                pipeline.addLast("handler", new RpcProxyHandler());
                            }
                        });
                //Netty是一个异步操作
                ChannelFuture future = boot.connect("localhost", 8080).sync();
                //发送代理商将协议请求对象发送给生产者
                future.channel().writeAndFlush(message).sync();
                future.channel().closeFuture().sync();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                group.shutdownGracefully();
            }
            return consumerHandler.getResponse();
        }
    }
}
